package com.zz.framework.core.ext.keybord

import android.app.Activity
import android.content.Context
import android.content.ContextWrapper
import android.content.pm.ApplicationInfo
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import android.view.WindowManager
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import androidx.annotation.RequiresApi
import androidx.fragment.app.Fragment
import com.zz.framework.core.ext.inputMethodManager
import java.lang.reflect.InvocationTargetException

/**
 * Copyright © 2019 zanezhao All rights reserved.
 * Created by zanezhao on 2019/12/1 9:25 PM.
 * @author: zanezhao 1820022519@qq.com
 * @version: V1.0
 */

//fun Activity.hideKeyboard() {
//    inputMethodManager?.hideSoftInputFromWindow((currentFocus ?: View(this)).windowToken, 0)
//    window!!.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN)
//    currentFocus?.clearFocus()
//}

fun Context.showKeyboard(et: EditText) {
    et.requestFocus()
    inputMethodManager?.showSoftInput(et, InputMethodManager.SHOW_IMPLICIT)
}

fun Context.hideKeyboard(view: View) {
    inputMethodManager?.hideSoftInputFromWindow(view.windowToken, 0)
}


/**
 *  Shows keyboard for EditText
 */
fun EditText.showKeyboard() {
    requestFocus()
    context.inputMethodManager?.showSoftInput(this, InputMethodManager.SHOW_FORCED)
}

/**
 *  Hides soft keyboard that is bound to the same [android.view.Window] that [this] [View]
 */
fun View.hideKeyboard() {
    findFocus()?.hideInternal()
}

/**
 *  Hides soft keyboard on Activity
 */
fun Activity.hideKeyboard2() {
    currentFocus?.hideInternal()
}
fun Fragment.hideKeyboard() {
    activity?.currentFocus?.hideInternal()
}

/**
 * @return true if soft keyboard shown on the screen.
 */
val Activity.isKeyboardShown: Boolean get() = keyboardHeight > 0

/**
 * @return true if soft keyboard shown on the screen.
 */
val Fragment.isKeyboardShown: Boolean? get() = keyboardHeight?.let { it > 0 }

/**
 * @return true if Keyboard shown on the screen.
 */
val View.isKeyboardShown: Boolean get() = keyboardHeight > 0

/**
 * @return soft keyboard visible height in pixels on the screen.
 */
val Activity.keyboardHeight: Int
    get() = inputMethodWindowVisibleHeight ?: window.decorView.inAppKeyboardHeight

/**
 * @return soft keyboard visible height in pixels on the screen.
 */
val Fragment.keyboardHeight: Int?
    get() = context?.inputMethodWindowVisibleHeight ?: view?.inAppKeyboardHeight

/**
 * @return soft keyboard visible height in pixels on the screen.
 */
val View.keyboardHeight: Int
    get() = context.inputMethodWindowVisibleHeight ?: inAppKeyboardHeight

/**
 * @param shown true if soft keyboard shown on the screen.
 * @param height soft keyboard visible height in pixels on the screen.
 */
typealias OnKeyboardShowChanged = (shown: Boolean, height: Int) -> Unit

/**
 * @param onChanged triggered when soft keyboard shown state and height changed
 * @param checkDelayMillis - interval of changing soft keyboard show state.
 *        Only for case when soft input mode is not adjust resize and sdk >= 21
 *
 *        什么周期调用cancel(),以免内存泄漏
 */
fun Activity.onKeyboardShowChanged(checkDelayMillis: Long = 300, onChanged: OnKeyboardShowChanged): Cancellable {
    val view = window.decorView
    val controller = ShowChangedController(onChanged)
    return if (softInputMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) {
        controller.update(view)
        view.onGlobalLayoutListener {
            controller.update(view)
        }
    } else {
        if (isDebugMode && Build.VERSION.SDK_INT < 21) {
            throw RuntimeException(
                "Can't define keyboard state for soft input mode != SOFT_INPUT_ADJUST_RESIZE and sdk < 21."
            )
        }
        uiHandler().postWithInterval(checkDelayMillis) {
            controller.update(view)
        }
    }
}


internal val Context.inputMethodManager: InputMethodManager
    get() {
        return getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
    }

internal val Activity.softInputMode: Int get() = window.attributes.softInputMode

internal val View.inAppKeyboardHeight: Int
    get() {
        checkCanDefineKeyboardHeight()
        val rootView = rootView as ViewGroup
        return rootView.getChildAt(0).paddingBottom
    }

internal fun View.checkCanDefineKeyboardHeight() {
    val softInputMode = context.asActivity()?.softInputMode
    if (context.isDebugMode && softInputMode != WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) {
        throw RuntimeException(
            "Can't define keyboard visible height for soft input mode != SOFT_INPUT_ADJUST_RESIZE."
        )
    }
}

internal fun View.hideInternal() {
    windowToken?.let {
        context.inputMethodManager?.hideSoftInputFromWindow(it, 0)
    }
}

internal val Context.inputMethodWindowVisibleHeight: Int?
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    get() = try {
        callMethod(inputMethodManager!!, "getInputMethodWindowVisibleHeight")
    } catch (e: NoSuchMethodException) {
        e.printStackTrace()
        null
    } catch (e: InvocationTargetException) {
        e.printStackTrace()
        null
    } catch (e: IllegalAccessException) {
        e.printStackTrace()
        null
    }

@Throws(
    NoSuchMethodException::class,
    InvocationTargetException::class,
    IllegalAccessException::class
)
private fun <T> callMethod(target: Any, methodName: String): T {
    val method = target.javaClass.getDeclaredMethod(methodName)
    method.isAccessible = true
    @Suppress("UNCHECKED_CAST")
    return method.invoke(target) as T
}


internal fun uiHandler() = Handler(Looper.getMainLooper())

internal fun Handler.postWithInterval(
    delayMillis: Long,
    block: () -> Unit
): Cancellable {
    val runnable = object : Runnable {
        override fun run() {
            block()
            postDelayed(this, delayMillis)
        }
    }
    runnable.run()
    return SafeCancellable { removeCallbacks(runnable) }
}


internal val Context.isDebugMode: Boolean
    get() = (applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE) != 0

internal fun Context?.asActivity(): Activity? {
    return when (this) {
        is Activity -> this
        is ContextWrapper -> this.baseContext.asActivity()
        else -> null
    }
}

internal fun View.onGlobalLayoutListener(
    block: () -> Unit
): Cancellable {
    val listener = ViewTreeObserver.OnGlobalLayoutListener { block() }
    viewTreeObserver.addOnGlobalLayoutListener(listener)

    return SafeCancellable {
        viewTreeObserver.removeOnGlobalLayoutListener(listener)
    }
}




